#include "stm32f4xx_ll_dma.h"
#include "stm32f4xx_ll_spi.h"
#include "stm32f4xx_ll_gpio.h"
#include "main.h"

#include "bsp_oled_io.h"

#define OLED_IO_DMA             DMA2
#define OLED_IO_DMA_CH          LL_DMA_STREAM_3
#define OLED_IO_SPI             SPI1

#define bsp_oled_dc_set()       LL_GPIO_SetOutputPin(OLED_DC_GPIO_Port, OLED_DC_Pin)
#define bsp_oled_dc_reset()     LL_GPIO_ResetOutputPin(OLED_DC_GPIO_Port, OLED_DC_Pin)

#define bsp_oled_cs_set()       LL_GPIO_SetOutputPin(OLED_CS_GPIO_Port, OLED_CS_Pin)
#define bsp_oled_cs_reset()     LL_GPIO_ResetOutputPin(OLED_CS_GPIO_Port, OLED_CS_Pin)

#define bsp_oled_res_set()      LL_GPIO_SetOutputPin(OLED_RES_GPIO_Port, OLED_RES_Pin)
#define bsp_oled_res_reset()    LL_GPIO_ResetOutputPin(OLED_RES_GPIO_Port, OLED_RES_Pin)


volatile uint32_t g_u4OLED_SPI_TX_Done;
volatile uint32_t g_u4CmdParamLen;
volatile const uint8_t *g_pu1Param;
/*repeat is only valid for RAM tx*/
volatile int32_t g_s4RepeatCnt;
volatile uint32_t g_u4ReloadTxLen;

__STATIC_INLINE void bsp_oled_io_setup_tx(volatile const uint8_t *pData,uint32_t len)
{
  LL_DMA_DisableStream(OLED_IO_DMA,OLED_IO_DMA_CH);
  LL_DMA_SetMemoryAddress(OLED_IO_DMA,OLED_IO_DMA_CH,(uint32_t)pData);
  LL_DMA_SetDataLength(OLED_IO_DMA,OLED_IO_DMA_CH,len);
  g_u4ReloadTxLen = len;
  LL_DMA_EnableStream(OLED_IO_DMA,OLED_IO_DMA_CH);
}

__STATIC_INLINE void bsp_oled_io_reload_txLen(void)
{
  LL_DMA_DisableStream(OLED_IO_DMA,OLED_IO_DMA_CH);
  LL_DMA_SetDataLength(OLED_IO_DMA,OLED_IO_DMA_CH,g_u4ReloadTxLen);
  LL_DMA_EnableStream(OLED_IO_DMA,OLED_IO_DMA_CH);  
}

void bsp_oled_io_init(void)
{
  bsp_oled_cs_reset();
  bsp_oled_res_reset();
  
  /*DMA and SPI basic congfiguration are placed at main.c*/
  
  LL_DMA_DisableStream (OLED_IO_DMA,OLED_IO_DMA_CH);
  /*Setup DMA dest address with SPI->DR*/
  LL_DMA_SetPeriphAddress(OLED_IO_DMA,OLED_IO_DMA_CH,(uint32_t)(&(OLED_IO_SPI->DR)));
  /*Enable DMA transfer complete interrupt*/
  LL_DMA_EnableIT_TC(OLED_IO_DMA,OLED_IO_DMA_CH);
  LL_DMA_EnableIT_HT(OLED_IO_DMA,OLED_IO_DMA_CH);
  
  /* Only enable ERR IT before DMA transfer comepleted.*/
  //LL_SPI_EnableIT_ERR(OLED_IO_SPI); 
  LL_SPI_DisableIT_TXE(OLED_IO_SPI);
  LL_SPI_EnableDMAReq_TX(OLED_IO_SPI);
  LL_SPI_Enable(OLED_IO_SPI);  
  
  g_u4OLED_SPI_TX_Done = 1;
  g_u4CmdParamLen = 0;
  
  LL_mDelay(10);
  
  bsp_oled_res_set();
  bsp_oled_cs_set();
}

void bsp_oled_spi_tx_done(void)
{
  //if (LL_SPI_IsActiveFlag_TXE(OLED_IO_SPI)
  if (LL_SPI_IsActiveFlag_BSY(OLED_IO_SPI) == 0){
    
    LL_SPI_DisableIT_TXE(OLED_IO_SPI);
    
    if(g_u4CmdParamLen == 0){
      /*Total transfer done*/
      bsp_oled_cs_set();
      g_u4OLED_SPI_TX_Done = 1;
    }
    else{
      /*send cmd parameter*/
      static uint32_t tmpLen;
      
      /*in case of dma has high priority*/
      /*that beore bsp_oled_spi_tx_done exit,bsp_oled_dma_tx_done occur*/
      tmpLen = g_u4CmdParamLen;
      g_u4CmdParamLen = 0;
      
      bsp_oled_dc_set();
      g_s4RepeatCnt = 1;
      bsp_oled_io_setup_tx(g_pu1Param,tmpLen);
    }
  }
}

void bsp_oled_dma_tx_done(void)
{
   LL_DMA_ClearFlag_HT3(OLED_IO_DMA);

   if(LL_DMA_IsActiveFlag_TC3(OLED_IO_DMA)){
     LL_DMA_ClearFlag_TC3(OLED_IO_DMA);
     
     if(g_s4RepeatCnt > 1 ){
       g_s4RepeatCnt--;
       /*continue to transfer*/
       bsp_oled_io_reload_txLen();
     }
     else{
       /*DMA transfer done,now should check if the last byte is sent by SPI*/
       LL_SPI_EnableIT_TXE(OLED_IO_SPI);
     }
   }
}


/*********************************************************/
/*********************************************************/
void bsp_oled_io_write_cmd_async(uint8_t cmd,const uint8_t *param,uint32_t len)
{
  /*MUST check, in case of async operation*/
  while (g_u4OLED_SPI_TX_Done == 0){;}
  
  g_u4OLED_SPI_TX_Done = 0;
  
  bsp_oled_cs_reset();  
  bsp_oled_dc_reset();//send cmd first 
  
  g_pu1Param = param;
  g_u4CmdParamLen = len;
  g_s4RepeatCnt = 1;
  
  bsp_oled_io_setup_tx(&cmd,1);
}

void bsp_oled_io_write_ram_async(const uint8_t *pData,uint32_t len,uint32_t repeatCnt)
{
  /*MUST check, in case of async operation*/
  while (g_u4OLED_SPI_TX_Done == 0){;}
  
  g_u4OLED_SPI_TX_Done = 0;
  
  bsp_oled_cs_reset();
  bsp_oled_dc_set();//send data
  
  g_u4CmdParamLen = 0;
  g_s4RepeatCnt = repeatCnt;
  
  bsp_oled_io_setup_tx(pData,len);
}


